package com.tyfspring.beans.factory;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import com.tyfspring.beans.Aware;
import com.tyfspring.beans.DisposableBean;
import com.tyfspring.beans.InitializingBean;
import com.tyfspring.beans.aware.BeanClassLoaderAware;
import com.tyfspring.beans.aware.BeanFactoryAware;
import com.tyfspring.beans.aware.BeanNameAware;
import com.tyfspring.beans.config.*;
import com.tyfspring.beans.exception.BeansException;
import com.tyfspring.beans.strategy.InstantiationStrategy;
import com.tyfspring.beans.strategy.SimpleInstantiationStrategy;
import com.tyfspring.beans.support.DisposableBeanAdapter;
import com.tyfspring.core.convert.ConversionService;
import com.tyfspring.util.ClassUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @Description 可解析Autowired注解的抽象类
 * @Author shallow
 * @Date 2023/3/27 16:10
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory{
    private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();

    private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        //如果bean需要代理，则直接返回代理对象
        Object bean = null;
        try {
            //判断是否返回代理bean对象
            bean = resolveBeforeInstantiation(beanName,beanDefinition);
            if (null!=bean){
                //是代理对象
                return bean;
            }
            //创建bean
            bean = doCreateBean(beanName,beanDefinition,args);
            //填充属性
            applyPropertyValues(beanName,bean,beanDefinition);
            //初始化
            bean = initializeBean(beanName,bean,beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }
        //注册实现了DisposableBean接口的Bean对象
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
        //判断scope
        if (beanDefinition.isSingleton()){
            registerSingleton(beanName, bean);
        }
        return bean;
}

    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        if (!beanDefinition.isSingleton()){
            return;
        }
        if(bean instanceof DisposableBean||StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())){
            registerDisposableBean(beanName,new DisposableBeanAdapter(bean,beanName,beanDefinition));
        }
    }

    public InstantiationStrategy getInstantiationStrategy(){
        return instantiationStrategy;
    }

    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }

    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition){
        //0.注入Aware方法
        if (bean instanceof Aware){
            if (bean instanceof BeanFactoryAware){
                ((BeanFactoryAware)bean).setBeanFactory(this);
            }
            if (bean instanceof BeanClassLoaderAware){
                ((BeanClassLoaderAware)bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanNameAware){
                ((BeanNameAware)bean).setBeanName(beanName);
            }
        }
        //1.实例化前，执行bean后处理器的before操作
        Object wrappedBean = applyBeanPostProcessorBeforeInitialization(bean, beanName);

        //执行Bean对象初始化方法
        try {
            invokeInitMethods(beanName,wrappedBean,beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
        }

        //3.执行bean后处理器的After操作
        wrappedBean=applyBeanPostProcessorBeforeInitialization(wrappedBean, beanName);
        return wrappedBean;
    }

    private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
        //1.实现接口InitializingBean
        if(bean instanceof InitializingBean){
            //属性注入后操作
            ((InitializingBean) bean).afterPropertiesSet();
        }
        //2.配置信息init-method 避免二次执行初始化
        String initMethodName = beanDefinition.getInitMethodName();
        if (StrUtil.isNotEmpty(initMethodName)){
            Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
            if(null == initMethod){
                throw new BeansException("Could not find an init method named '" +
                        initMethodName + "' on bean with name '" + beanName + "'");
            }
            initMethod.invoke(bean);
        }
    }

    /**
     * 执行 InstantiationAwareBeanPostProcessor的方法，如果bean需要代理，直接返回代理对象
     * 也就是该后处理器中bean实例化之前的增强方法
     * @param beanName
     * @param beanDefinition
     * @return
     */
    protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
        Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);
        if (bean != null) {
            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        }
        return bean;
    }

    /**
     * 对所有后处理器，调用其中的postProcessBeforeInstantiation 实例化前增强方法
     * @param beanClass
     * @param beanName
     * @return
     */
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) {
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
                Object result = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }

        return null;
    }

    protected Object doCreateBean(String beanName, BeanDefinition beanDefinition, Object[] args){
        Constructor constructorToUse = null;
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor ctor : declaredConstructors) {
            if (null!=args&&ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }
        return instantiationStrategy.instantiate(beanDefinition,beanName,constructorToUse,args);
    }

    /**
     * 为bean填充属性
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition){
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();
                if (value instanceof BeanReference) {
                    //beanA依赖beanB,先实例化beanB
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }else{
                    //类型转换
                    Class<?> sourceType = value.getClass();
                    Class<?> targetType = (Class<?>) TypeUtil.getFieldType(bean.getClass(), name);
                    ConversionService conversionService = getConversionService();
                    if (conversionService!=null){
                        if (conversionService.canConvert(sourceType, targetType)){
                            value = conversionService.convert(value,targetType);
                        }
                    }
                }
                BeanUtil.setFieldValue(bean,name,value);
            }
        } catch (BeansException e) {
            throw new BeansException("Error setting property values for bean: " + beanName, e);
        }
    }

    @Override
    public Object applyBeanPostProcessorBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if(null == current){
                return result;
            }
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if(null == current){
                return result;
            }
            result = current;
        }
        return result;
    }

    public ClassLoader getBeanClassLoader() {
        return beanClassLoader;
    }
}
